package neves.android.etmg.tilemap;

import java.util.LinkedList;

import neves.android.etmg.map.GameMap;
import neves.android.etmg.map.MapRender;
import neves.android.etmg.sprite.Creature;
import neves.android.etmg.sprite.GameItem;
import neves.android.etmg.sprite.GameSprite;
import neves.android.etmg.sprite.MovableObject;
import neves.android.etmg.sprite.SpriteType;

import android.graphics.Bitmap;
import android.util.Log;

public class TileMap implements GameMap {

	// 0:wall , 1:empty;
	private int[][] intTiles;

	private Bitmap[][] tiles;

	// private int[] roomBoundry = new int[4]; // minx,miny,maxx,maxy

	private int noneWallTileCount = 0;

	// all movable objects
	private LinkedList<GameSprite> mObjs;

	// all items
	private LinkedList<GameItem> items;
	
	// all movable objects
	private LinkedList<Creature> creatures;

	// all room in this map
	private LinkedList<TileMapRoom> rooms;

	// all passage in this map
	private LinkedList<TileMapRoom> passages;
	
	private Creature player;

	private GameSprite stair;
	
	private MapRender mapRender;


	
	// the pos presents the map draw start point
	private GameSprite drawViewer = new GameSprite("ViewPoint",
			SpriteType.OmniType);

	public TileMap(int width, int height) {

		CreateNewMap(width, height);

		// mObjs.add(drawViewer);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see neves.android.etmg.map.GameMap#GetMapRender()
	 */
	public MapRender GetMapRender() {
		return mapRender;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see neves.android.etmg.map.GameMap#SetMapRender(neves.android.etmg.map.MapRender )
	 */
	public void SetMapRender(MapRender nmr) {
		mapRender = nmr;

		mapRender.SetMap(this);

	}
	
	
	public void SetStair(GameSprite theStair){
		
		stair = theStair;
		
		AddSprite(theStair);
		
	}
	
	public GameSprite GetStair(){
		return stair;
	}

	@Override
	public void AddSprite(GameSprite sprite) {
		// TODO Auto-generated method stub
		mObjs.add(sprite);
	}

	@Override
	public LinkedList<GameSprite> GetSprites() {
		// TODO Auto-generated method stub
		return mObjs;
	}

	public LinkedList<TileMapRoom> GetRooms() {
		// TODO Auto-generated method stub
		return rooms;
	}

	public TileMapRoom InWhichRoom(int[] pos) {
		TileMapRoom thisRoom = null;
		for (TileMapRoom room : rooms) {
			if (room.IsInRoom(pos[0], pos[1])) {
				thisRoom = room;
				break;
			}
		}
		return thisRoom;
	}

	public LinkedList<TileMapRoom> GetPassages() {
		// TODO Auto-generated method stub
		return passages;
	}

	public void CreateNewMap(int width, int height) {

		intTiles = new int[width][height];

		tiles = new Bitmap[width][height];

		mObjs = new LinkedList<GameSprite>();
		
		items = new LinkedList<GameItem>();

		creatures = new LinkedList<Creature>();

		rooms = new LinkedList<TileMapRoom>();

		passages = new LinkedList<TileMapRoom>();

		noneWallTileCount = 0;

	}

	// public boolean MakeVerticalLine(){}

	public boolean MakeARoom(int startx, int starty, int roomWidth,
			int roomHeight) {

		if (isRoomLegal(startx, starty, roomWidth, roomHeight)) {

			if (CreateRoom(startx, starty, roomWidth, roomHeight)) {
				rooms.add(new TileMapRoom(startx, starty,roomWidth,roomHeight));
				return true;
			}
		}
		return false;
	}

	public boolean MakeAPassage(int startx, int starty, int passageLength,
			boolean isVertical) {

		int roomWidth = passageLength;
		int roomHeight = 1;

		if (isVertical) {
			roomWidth = 1;
			roomHeight = passageLength;
		}
//		if (IsPassageAdjacent(sx, sy, rw, rh)) {		return false;}
		if(isPassageLegal(startx, starty, roomWidth, roomHeight)){
		
			Log.w("ETMG", "pas:"+Integer.toString(startx)+","+Integer.toString(starty)+","+Integer.toString(passageLength)+","+Boolean.toString(isVertical));
									
			int[][] adjPoints = null ;
			
			if(passageLength>2){
				TileMapRoom tunnelRoom = new TileMapRoom(startx,starty,roomWidth,roomHeight);
				adjPoints= this.CheckAdjacentPoints(tunnelRoom);
			}
			
			if(adjPoints==null){
			
				if (CreateRoom(startx, starty, roomWidth, roomHeight)) {
					passages.add(new TileMapRoom(startx, starty, roomWidth, roomHeight));
					
					return true;
				}
			}else{  // have adj point need to fix the passage
				
				
				
				int[] endp = new int[]{startx+roomWidth-1, starty+roomHeight-1};
				
				FixAdjPassage( new int[]{startx,starty},endp,passageLength,isVertical,adjPoints );
				
							
				
			}
		}
		return false;
	}

	public void SetTile(int x, int y, Bitmap tile) {

		tiles[x][y] = tile;
	}

	public int[] GetMapSize() {
		return new int[] { intTiles.length, intTiles[0].length };
	}

	public int GetMapMaxX() {
		return intTiles.length;
	}

	public int GetMapMaxY() {
		return intTiles[0].length;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeneves.android.etmg.map.GameMap#SetPlayer(neves.android.etmg.sprite.
	 * MovableObject)
	 */
	public void SetPlayer(Creature newPlayer) {
		this.player = newPlayer;
		mObjs.add(player);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see neves.android.etmg.map.GameMap#GetPlayer()
	 */
	public Creature GetPlayer() {
		return player;

	}

	public GameSprite GetViewer() {
		return drawViewer;

	}

	public void SetViewPos(int vx, int vy) {
		drawViewer.SetPos(vx, vy);
	}

	public void SetIntTile(int posX, int posY, int tileInt) {
		if (!isOutofBound(posX, posY)) {

			UpdateMapInfo(posX, posY, tileInt);

			intTiles[posX][posY] = tileInt;

		}
	}

	public int GetIntTile(int posX, int posY) {

		if (isOutofBound(posX, posY)) {
			return -1;
		} else {
			return intTiles[posX][posY];
		}

	}

	public boolean isOutofBound(int posX, int posY) {
		if (posX < 0 || posX >= intTiles.length || posY < 0
				|| posY >= intTiles[0].length) {
			return true;
		}
		return false;
	}

	public int[] RandomRoomRandomPos() {
		int[] apos = new int[2];

		if (rooms.size() > 0) {

			int temRandomNum = (int) ((Math.random() * 100) % rooms.size());

			TileMapRoom curRoom = rooms.get(temRandomNum);

			int roomWid = curRoom.Width();
			int roomHig = curRoom.Height();

			int rx = (int) ((Math.random() * 100) % roomWid);
			int ry = (int) ((Math.random() * 100) % roomHig);

			apos[0] = rx + curRoom.Left();
			apos[1] = ry + curRoom.Top();

			// Log.w("ETMG", apos[0]+" "+apos[1]);

		}
		return apos;
	}

	public boolean isWallTile(int x, int y) {
		if (intTiles[x][y] == 0) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isCreatureOccupy(int x, int y) {
		for (int temi = 0; temi < creatures.size(); temi++) {
			int[] curPos = creatures.get(temi).GetPos();
			if (curPos[0] == x && curPos[1] == y) {
				return true;
			}
		}
		return false;
	}

	public boolean isSpriteOccupy(int x, int y) {
		for (int temi = 0; temi < mObjs.size(); temi++) {
			int[] curPos = mObjs.get(temi).GetPos();
			if (curPos[0] == x && curPos[1] == y) {
				return true;
			}
		}
		return false;
	}

	public boolean isLegalPos(int x, int y, int spriteType) {
		if (isOutofBound(x, y)) {
			return false;
		}
		if (spriteType == SpriteType.ItemType) {
			if (isWallTile(x, y))
				return false;
			// item can't overlap on item
			if (isSpriteOccupy(x, y) && !isCreatureOccupy(x, y))
				return false;
		} else if (spriteType == SpriteType.FlyCreatureType) {
			if (isWallTile(x, y))
				return false;
		} else if (spriteType == SpriteType.CreatureType) {
			if (isWallTile(x, y))
				return false;

			if (isCreatureOccupy(x, y))
				return false;
		}

		return true;
	}
	public boolean isPassageLegal(int sx, int sy, int rw, int rh) {

		if (isOutofBound(sx, sy) || isOutofBound(sx + rw, sy + rh)) {
			return false;
		}



		return true;
	}
	public boolean isRoomLegal(int sx, int sy, int rw, int rh) {

		if (isOutofBound(sx, sy) || isOutofBound(sx + rw, sy + rh)) {
			return false;
		}

		if (IsRoomOverlap(sx, sy, rw, rh)) {
			return false;
		}

		if (IsPassageOverlap(sx, sy, rw, rh)) {
			return false;
		}

		return true;
	}
	

	protected boolean IsRoomOverlap(int sx, int sy, int rw, int rh) {

		for (int temi = 0; temi < rooms.size(); temi++) {
			TileMapRoom curRoom = rooms.get(temi);
			if (curRoom.IsOverlap(sx, sy, rw, rh))
				return true;
		}

		return false;
	}

	protected boolean IsPassageOverlap(int sx, int sy, int rw, int rh) {

		for (int temi = 0; temi < passages.size(); temi++) {
			TileMapRoom curRoom = passages.get(temi);
			if (curRoom.IsOverlap(sx, sy, rw, rh))
				return true;
		}

		return false;
	}
	
	protected void FixAdjPassage(int[] startp, int[] endp, int passageLength, boolean isVertical, int[][] adjPoints){

		//  if adjPoints.length == passageLength then do nothing!!
		if (adjPoints.length == passageLength){
			
		}else{
			boolean headAdj = false;
			boolean endAdj = false;
			
			Log.w("ETMG", "fadj:"+Integer.toString(startp[0])+","+Integer.toString(startp[1])+","+Integer.toString(endp[0])+","+Integer.toString(endp[1])+","+Integer.toString(passageLength)+","+Boolean.toString(isVertical));
				
			for(int temi = 0 ; temi<adjPoints.length ; temi++){
				
				if((adjPoints[temi][0]==startp[0])&&(adjPoints[temi][1]==startp[1])){
					headAdj = true;
				
				}else if((adjPoints[temi][0]==endp[0])&&(adjPoints[temi][1]==endp[1])){
					endAdj = true;

				}else{

				}
			}
			
			if(headAdj){
				
				// fix the start point problem
				this.SetIntTile(startp[0], startp[1], 0);
				
				if(isVertical){
					startp[1]+= (adjPoints.length-1);
				}else{
					startp[0]+= (adjPoints.length-1);
				}
				
				passageLength -= (adjPoints.length-1);
				
				this.MakeAPassage(startp[0], startp[1], passageLength, isVertical);
				
				
				
				Log.w("ETMG", "ha:"+Integer.toString(startp[0])+","+Integer.toString(startp[1])+","+Integer.toString(passageLength)+","+Boolean.toString(isVertical));
				
				
			}else if(endAdj){
				
				// fix the start point problem
				
				
				passageLength -= (adjPoints.length-1);
				
				this.MakeAPassage(startp[0], startp[1], passageLength, isVertical);
				
				Log.w("ETMG", "ea:"+Integer.toString(startp[0])+","+Integer.toString(startp[1])+","+Integer.toString(passageLength)+","+Boolean.toString(isVertical));
				
				
			}else{  // split into two passages
				Log.w("ETMG", "na:"+Integer.toString(startp[0])+","+Integer.toString(startp[1])+","+Integer.toString(passageLength)+","+Boolean.toString(isVertical));
					/*
					for(int tems = 0 ; tems<adjPoints.length ; tems++){
						//Log.w("ETMG", "adj:"+Integer.toString(adjLine[tems][0])+","+Integer.toString(adjLine[tems][0]));
						this.SetIntTile(adjPoints[tems][0], adjPoints[tems][1], 2);
					}	
					*/
					
					if(isVertical){   // x should be fixed
						
						int pasLength = adjPoints[0][1] - startp[1]+1;
						
						this.MakeAPassage(startp[0], startp[1], pasLength, isVertical);
						
						int newLiney = adjPoints[adjPoints.length-1][1];
						
						pasLength = endp[1] - newLiney;
						
						this.MakeAPassage(startp[0], newLiney, pasLength, isVertical);
						
					}else{
						int pasLength = adjPoints[0][0] - startp[0]+1;
						
						this.MakeAPassage(startp[0], startp[1], pasLength, isVertical);
						
						int newLinex = adjPoints[adjPoints.length-1][0];
						
						pasLength = endp[0] - newLinex;
						
						this.MakeAPassage(newLinex,startp[1], pasLength, isVertical);
						
						
					}
					
			}
			
						
		}
		
		
		
	}
	
	protected int[][] CheckAdjacentPoints(TileMapRoom tunnelRoom) {
		
		// check if adjacent to Rooms
		int[][] adjLine = FindAdjacentPoint(tunnelRoom,rooms);
		
		// check if adjacent to other passages
		if(adjLine==null){
			adjLine = FindAdjacentPoint(tunnelRoom,passages);
		}
		
		return adjLine;
		
		//return false;
	}
	
	protected int[][] FindAdjacentPoint(TileMapRoom tunnelRoom, LinkedList<TileMapRoom> targetRooms){
		
		int[][] adjPoints = null;
				
		for (int temi = 0; temi < targetRooms.size(); temi++) {
			TileMapRoom curRoom = targetRooms.get(temi);
			
			adjPoints = curRoom.BounderOverlapPoints(tunnelRoom);
			
			if(adjPoints.length>2){
				return adjPoints;
			}
			/*
			else if (adjPoints.length>1){   // only two point
				
				// check if two point are adjacent
				if(( Math.abs(adjPoints[0][0]-adjPoints[1][0])==1  ) 
						|| (Math.abs(adjPoints[0][1]-adjPoints[1][1])==1 ) ){
					return adjPoints;
				} 
			}else{
				
			}
			*/
		}
		return null;
	}


	protected boolean CreateRoom(int startx, int starty, int roomWidth,	int roomHeight) {

		int posX, posY;
		for (int temx = 0; temx < roomWidth; temx++) {
			posX = startx + temx;
			for (int temy = 0; temy < roomHeight; temy++) {
				posY = starty + temy;
				//if (!isOutofBound(posX, posY)) {
					SetIntTile(posX, posY, 1);
				//}

			}
		}
		return true;
	}

	protected void GetAllRoomTiles() {
		int result = 0;
		for (int temi = 0; temi < rooms.size(); temi++) {
			TileMapRoom curRoom = rooms.get(temi);

			result += curRoom.GetRoomSize();

		}

	}

	protected void UpdateMapInfo(int posX, int posY, int tileInt) {
		UpdateNoneWallTileCount(posX, posY, tileInt);
	}

	// check if is none wall tile updates the [noneWallTileCount]
	protected void UpdateNoneWallTileCount(int posX, int posY, int tileInt) {

		if (intTiles[posX][posY] == 0 && (tileInt != 0)) {
			noneWallTileCount++;
		} else if ((intTiles[posX][posY] != 0) && (tileInt == 0)) {
			noneWallTileCount--;
		}

	}

	@Override
	public int[] MapSize() {
		// TODO Auto-generated method stub
		return new int[] { intTiles[0].length, intTiles.length };
	}

	@Override
	public void AddCreature(Creature creature) {
		creatures.add(creature);
		mObjs.add(creature);
		// TODO Auto-generated method stub

	}

	@Override
	public LinkedList<Creature> GetCreatures() {
		// TODO Auto-generated method stub
		return creatures;
	}

	@Override
	public void AddItem(GameItem sprite) {
		items.add(sprite);
		mObjs.add(sprite);
		// TODO Auto-generated method stub
		
	}

	@Override
	public LinkedList<GameItem> GetItems() {
		// TODO Auto-generated method stub
		return items;
	}

}
